import { some } from "lodash-es";
import { toValue } from "vue";

function isNumber(val: any) {
  return !isNaN(Number(val));
}

type ExpressionFunction = (v1: any, v2: any) => boolean;

interface ExpressionOperatorMap {
  [key: string]: ExpressionFunction;
}

const expressionFunctions: ExpressionOperatorMap = {
  eq(v1, v2) {
    if (!v1) {
      return false;
    }
    if (isNumber(v1)) {
      v2 = +v2;
    }
    return Array.isArray(v1) ? arraysEqualIgnoreType(v1, v2) : v1 == v2;
  },
  ne(v1, v2) {
    if (isNumber(v1)) {
      v2 = +v2;
    }
    return Array.isArray(v1) ? !arraysEqualIgnoreType(v1, v2) : v1 != v2;
  },
  gt(v1, v2) {
    if (isNumber(v1)) {
      v2 = +v2;
    }
    if (!Number(v1) || !Number(v2)) {
      return false;
    }
    return v1 > v2;
  },
  ge(v1, v2) {
    if (isNumber(v1)) {
      v2 = +v2;
    }
    if (!Number(v1) || !Number(v2)) {
      return false;
    }
    return v1 >= v2;
  },
  lt(v1, v2) {
    if (isNumber(v1)) {
      v2 = +v2;
    }
    if (!Number(v1) || !Number(v2)) {
      return false;
    }
    return v1 < v2;
  },
  le(v1, v2) {
    if (isNumber(v1)) {
      v2 = +v2;
    }
    if (!Number(v1) || !Number(v2)) {
      return false;
    }
    return v1 <= v2;
  },
  like(v1, v2) {
    if (!v1) {
      return false;
    }
    if (Array.isArray(v1)) {
      return includesValue(v1, v2);
    }
    return v1.indexOf(v2) > -1;
  },
  notLike(v1, v2) {
    if (!v1) {
      return false;
    }
    if (Array.isArray(v1)) {
      return !includesValue(v1, v2);
    }
    return v1.indexOf(v2) == -1;
  },
  isNull(v1: any) {
    return checkNull(v1);
  },
  notNull(v1: any) {
    return !checkNull(v1);
  }
};

function checkNull(v1: any) {
  if (v1 === null || v1 === undefined) {
    return true;
  }

  if (Array.isArray(v1) && v1.length === 0) {
    return true;
  }

  if (v1 !== null && typeof v1 === "object" && !Array.isArray(v1) && Object.keys(v1).length === 0) {
    return true;
  }

  return typeof v1 === "string" && v1.length === 0;
}

function includesValue(array: any[], value: any) {
  return some(array, (item: any) => item == value);
}

function arraysEqualIgnoreType(arr1: any[], arr2: any): boolean {
  if (!Array.isArray(arr2)) {
    if (typeof arr2 === "string") {
      arr2 = arr2.split(",");
    } else {
      arr2 = [arr2];
    }
  }
  if (arr1.length !== arr2.length) {
    return false;
  }

  const sortedArr1 = arr1.map(String).sort();
  const sortedArr2 = arr2.map(String).sort();

  for (let i = 0; i < sortedArr1.length; i++) {
    if (sortedArr1[i] !== sortedArr2[i]) {
      return false;
    }
  }
  return true;
}

/**
 * 执行表达式是否成立
 * @param type 表达式类型
 * @param v1  填写值
 * @param v2  表达式值 也就是条件值
 * @returns 是否成立
 */
export function evaluateExpression(type: string, v1: any, v2: any): boolean {
  if (!type) {
    return true;
  }
  const func = expressionFunctions[type];
  if (func) {
    return func(v1, v2);
  }
  throw new Error(`Unsupported expression type: ${type}`);
}

/**
 * 验证单个条件
 * @param formData 表单数据
 * @param condition 具体条件
 * @returns 条件是否成立
 */
function validateConcreteCondition(formData: Record<string, any>, condition: ConcreteCondition): boolean {
  const { when, operator, value } = condition;
  const v1 = toValue(formData[when]);
  return evaluateExpression(operator, v1, value);
}

/**
 * 验证可见性表达式
 * @param formData 表单数据
 * @param expr 可见性表达式
 * @returns 表达式是否成立
 */
function validateVisibilityExpr(formData: Record<string, any>, expr: VisibilityExpr): boolean {
  if (!expr) {
    return true;
  }

  // 处理具体条件
  if ("when" in expr) {
    return validateConcreteCondition(formData, expr as ConcreteCondition);
  }

  // 处理 and 操作符
  if ("and" in expr) {
    return (expr as { and: VisibilityExpr[] }).and.every(subExpr => validateVisibilityExpr(formData, subExpr));
  }

  // 处理 or 操作符
  if ("or" in expr) {
    return (expr as { or: VisibilityExpr[] }).or.some(subExpr => validateVisibilityExpr(formData, subExpr));
  }

  // 处理 not 操作符
  if ("not" in expr) {
    return !validateVisibilityExpr(formData, (expr as { not: VisibilityExpr }).not);
  }

  return true;
}

/**
 * 验证表单逻辑是否成立
 * @param formData 表单数据
 * @param visibilityExpr 可见性表达式
 * @returns 是否成立
 */
export function validateFormLogic(formData: Record<string, any>, visibilityExpr: VisibilityExpr): boolean {
  if (!visibilityExpr) {
    return true;
  }
  return validateVisibilityExpr(formData, visibilityExpr);
}
